﻿#include "Timer_v1.h"

#include <iostream>
#include <stdexcept>

namespace LH
{
	namespace V1
	{
		bool Timer::reschedule(
			ITimer::TimerID id,
			std::chrono::milliseconds msecs,
			bool repeat)
		{
			if (msecs == NO_DELAY && repeat)
			{
				throw std::logic_error{ "Does't allow repeating every 0 millisecond." };
			}
			// 使用旧的节点相关参数构建新的节点，旧的节点从哈希表中删除

			// need lock 
			std::lock_guard<std::mutex> locker{m_mtx};
			auto it = m_timerNodeHashMap.find(id);
			if (it != m_timerNodeHashMap.end()) {
				auto& spNode = it->second;
				auto spNewNode = std::make_shared<TimerNode>();

				spNewNode->id = id;
				spNewNode->callback = spNode->callback;
				spNewNode->datum = spNode->datum;
				if (!repeat) {
					spNewNode->datum = Datum::NOT_SET;
				}
				spNewNode->tp = std::chrono::steady_clock::now() + msecs;

				spNewNode->interval = repeat ? msecs : NO_DELAY;

				m_timerNodeHashMap.erase(it);
				m_timerNodeHashMap[id] = spNewNode;
				m_timerNodeQueue.push(spNewNode);
				return true;
			}
			throw std::logic_error{ "Cannot reschedule non-existent TimerID." };
		}

		void Timer::cancel(TimerID id)
		{ 
			// need lock
			std::lock_guard<std::mutex> locker{ m_mtx };
			m_timerNodeHashMap.erase(id);  // 从哈希表中删除即可
		}

		void Timer::cancelAll()
		{ 
			// need lock
			std::lock_guard<std::mutex> locker{ m_mtx };
			TimerNodesQueue newQueue;
			m_timerNodeQueue.swap(newQueue); // 清空队列
			m_timerNodeHashMap.clear();// 清空哈希表
		}

		ITimer::TimerID
			Timer::doSchedule(TimerListener listener,
				std::chrono::milliseconds msecs,
				std::chrono::milliseconds delay,
				bool repeat, Datum repeatMode)
		{
			if (msecs == NO_DELAY && repeat)
			{
				throw std::logic_error{ "Does't allow repeating every 0 millisecond." };
			}

			auto now = std::chrono::steady_clock::now();
			auto&& spNode = std::make_shared<TimerNode>();
			spNode->callback = listener;
			spNode->tp = now + (delay == NO_DELAY ? msecs : delay);
			spNode->id = calculateTimerID();
			if (repeat) {
				spNode->datum = repeatMode;
				spNode->interval = std::chrono::milliseconds{ msecs };
			}

			// need lock
			std::lock_guard<std::mutex> locker{ m_mtx };
			m_timerNodeQueue.push(spNode);
			m_timerNodeHashMap[spNode->id] = spNode;

			return spNode->id;

		}

		bool Timer::fireTimer(const std::chrono::steady_clock::time_point& tpNow)
		{
			bool ret{ false };
			while (!m_timerNodeQueue.empty())
			{

				std::lock_guard<std::mutex> locker{ m_mtx };
				if (auto spNode = m_timerNodeQueue.top().lock())
				{
					if (spNode->tp > tpNow) break;

					m_timerNodeQueue.pop();
					if (spNode->interval < NO_DELAY)
					{
						std::cerr << "Invalid TimerNode, TimerID[" << spNode->id << "]." << std::endl;
						continue;
					}

					if (spNode->callback)
					{
						spNode->callback(spNode->id);
						ret = true;
					}

					switch (spNode->datum)
					{
					case Datum::FROM_NOW:
					{
						auto spNewNode = std::make_shared<TimerNode>(*spNode);
						spNewNode->tp = tpNow + spNewNode->interval;
						m_timerNodeQueue.push(spNewNode);
						m_timerNodeHashMap[spNewNode->id] = spNewNode;

					}
					break;
					case Datum::FROM_LAST_TIMER:
					{
						auto spNewNode = std::make_shared<TimerNode>(*spNode);
						spNewNode->tp += spNewNode->interval;
						m_timerNodeQueue.push(spNewNode);
						m_timerNodeHashMap[spNewNode->id] = spNewNode;
					}
					break;
					default:
						m_timerNodeHashMap.erase(spNode->id);
						break;
					}

				}
				else
				{
					m_timerNodeQueue.pop();
				}
			}
			return ret;
		}
		 
		int32_t Timer::calculateTimeout(const std::chrono::steady_clock::time_point& now)
		{
			if (m_timerNodeQueue.empty()) return -1;
			std::lock_guard<std::mutex> locker{ m_mtx };
			auto spNode = m_timerNodeQueue.top().lock();
			if (!spNode) return -1;
			return spNode->tp > now ?
				std::chrono::duration_cast<std::chrono::milliseconds>
				((spNode->tp - now)).count() : 0;
		}

	}// namespace Timer_V1
} // namespace LH
